<?php

/**
 * 'session'=>array(
 *    'class'=>'ext.EMongoDbHttpSession.EMongoDbHttpSession',
 * ),
 */
class EMongoDbHttpSession extends CHttpSession
{
    public $collectionName = "session";
    public $idColumn       = 'id';
    public $dataColumn     = "data";
    public $expireColumn   = "expire";
    public $fsyncFlag;
    public $safeFlag;
    
    protected function getData($id)
    {
        return $this->getCollection()->findOne(
            array($this->idColumn=>$id), 
            array($this->dataColumn)
        );
    }

    protected function getExipireTime()
    {
        return time() + $this->getTimeout();
    }

    /**
     * Returns a value indicating whether to use custom session storage.
     * This method overrides the parent implementation and always returns true.
     * @return boolean whether to use custom storage.
     */
    public function getUseCustomStorage()
    {
        return true;
    }

    /**
     * Session open handler.
     * Do not call this method directly.
     * @param string $savePath session save path
     * @param string $sessionName session name
     * @return boolean whether session is opened successfully
     */
    public function openSession($savePath, $sessionName)
    {
        $this->gcSession(0);
    }

    /**
     * Session read handler.
     * Do not call this method directly.
     * @param string $id session ID
     * @return string the session data
     */
    public function readSession($id)
    {
        $row = $this->getData($id);
        return is_null($row) ? '' : $row[$this->dataColumn];
    }

    /**
     * Session write handler.
     * Do not call this method directly.
     * @param string $id session ID
     * @param string $data session data
     * @return boolean whether session write is successful
     */
    public function writeSession($id, $data)
    {
        return $this->getCollection()->update(
            array($this->idColumn=>(string)$id), 
            array(
                $this->dataColumn  =>$data, 
                $this->expireColumn=>$this->getExipireTime(), 
                $this->idColumn    =>(string)$id
            ), 
            array(
                'fsync'=>$this->getFsyncFlag(),
                'safe'=>$this->getSafeFlag(),
                'multiple'=>false,
                'upsert'=>true,
            )
        );
    }

    /**
     * Session destroy handler.
     * Do not call this method directly.
     * @param string $id session ID
     * @return boolean whether session is destroyed successfully
     */
    public function destroySession($id)
    {
        return $this->getCollection()->remove(
            array($this->idColumn=>$id),
            array(
                'fsync'=>$this->getFsyncFlag(),
                'safe' =>$this->getSafeFlag(),
            )
        );
    }

    /**
     * Session GC (garbage collection) handler.
     * Do not call this method directly.
     * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
     * @return boolean whether session is GCed successfully
     */
    public function gcSession($maxLifetime)
    {
        return $this->getCollection()->remove(
            array($this->expireColumn=>array('$lt'=>time())),
            array(
                'fsync'=>$this->getFsyncFlag(),
                'safe' =>$this->getSafeFlag(),
            )
        );
    }

    /**
     * Updates the current session id with a newly generated one .
     * Please refer to {@link http://php.net/session_regenerate_id} for more details.
     * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
     * @since 1.1.8
     */
    public function regenerateID($deleteOldSession = false)
    {
        $oldId = session_id();
        parent::regenerateID(false);
        $newId = session_id();
        $row = $this->getData($oldId);
        if (is_null($row))
        {
            $this->getCollection()->insert(array(
                $this->idColumn=>$newId, 
                $this->expireColumn=>$this->getExipireTime()), 
                array(
                    'fsync'=>$this->getFsyncFlag(),
                    'safe' =>$this->getSafeFlag(),
                )
            );
        }
        else if ($deleteOldSession)
        {
            $this->getCollection()->update(
                array($this->idColumn=>$oldId), 
                array($this->idColumn=>$newId), 
                array(
                    'fsync'=>$this->getFsyncFlag(),
                    'safe' =>$this->getSafeFlag(),
                    'upsert'=>true,
                    'multiple'=>false,
                )
            );
        }
        else
        {
            $row[$this->idColumn] = $newId;
            unset($row['_id']);
            $this->getCollection()->insert($row, 
                array(
                    'fsync'=>$this->getFsyncFlag(),
                    'safe' =>$this->getSafeFlag(),
                )
            );
        }
    }
    
    public function getCollection()
    {
        static $collection = null;
        if(empty($collection))
            $collection = $this->getMongoDBComponent()->getDbInstance()->selectCollection($this->collectionName);
        
        return $collection;
    }
    
    public function getFsyncFlag()
    {
        if(null === $this->fsyncFlag)
            $this->fsyncFlag = $this->getMongoDBComponent()->fsyncFlag;
        
        return $this->fsyncFlag;
    }
    
    public function getSafeFlag()
    {
        if(null === $this->safeFlag)
            $this->safeFlag = $this->getMongoDBComponent()->safeFlag;
        
        return $this->safeFlag;
    }
       
    public function getMongoDBComponent()
    {
        return Yii::app()->getComponent('mongodb');
    }
}